home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacHack 1997
/
MacHack 1997.toast
/
Hacks
/
Hacks ’96
/
Venus
/
Other .h files for easy ref
/
image.h
< prev
next >
Wrap
Text File
|
1996-06-22
|
20KB
|
630 lines
// This may look like C code, but it is really -*- C++ -*-
/*
************************************************************************
*
* Grayscale Image
*
* The image is represented as a Pixmap, i.e. a matrix of pixels
* each of them specifies the gray level at a particular point
*
* The header file defines arithmetical and all other operations
* permitted on the grayscale image
*
* $Id: image.h,v 1.16 1995/03/17 18:00:57 oleg Exp oleg $
*
************************************************************************
*/
#ifndef __GNUC__
#pragma once
#endif
#ifndef _image_h
#define _image_h
#ifdef __GNUC__
#pragma interface
#endif
#include "myenv.h"
#include "std.h"
typedef unsigned short GRAY; // Pixel type
typedef signed short GRAY_SIGNED; // Pixel type, signed
const int GRAY_MAXBIT = 8*sizeof(GRAY); // Max no of bits per pixel
const int GRAY_MAXVAL = ((1<<GRAY_MAXBIT)-1);
typedef unsigned int card; // Better be short, but compilers
// seem to like 'int' better
class Rectangle;
class rowcol;
class Extrema;
class EndianIn;
class IMAGE;
class FilterIt;
// A class to do a specific operation on
// every pixel element regardless of its
// position. The matrix is traversed
// row-by-row
class PixelPrimAction
{
friend class IMAGE;
virtual void operation(GRAY& pixel) = 0;
// Those are'n implemented; but since they're
// private, it forbids the assignement
// PixelPrimAction(const PixelPrimAction&);
PixelPrimAction& operator= (const PixelPrimAction&);
};
// A class to do a specific operation on
// every matrix element as the matrix
// is efficiently traversed (row-by-row)
class PixelAction
{
friend class IMAGE;
virtual void operation(GRAY& pixel) = 0;
protected:
card nrows, ncols; // These are set up by IMAGE::apply() function
// and available to PixelAction::operation()
card row, col; // position of the element being passed to
// 'operator()'
private: // Those are'n implemented; but since they're
// private, it forbids the assignement
// PixelAction(const PixelAction&);
PixelAction& operator= (const PixelAction&);
};
// Lazy image constructor
// It's highly recommended a function never
// return IMAGE itself. Use LazyImage
// instead
class LazyImage
{
friend class IMAGE;
virtual void fill_in(IMAGE& im) const = 0;
LazyImage(const LazyImage&); // Don't implement to forbid assignment
LazyImage& operator = (const LazyImage&);
protected:
card nrows, ncols, depth;
public:
LazyImage(const card _nrows, const card _ncols, const card _depth)
: nrows(_nrows), ncols(_ncols), depth(_depth) {}
};
class IMAGE
{
friend class Rectangle;
friend class Extrema;
friend class FilterIt;
private: // Private part
int valid_code; // Validation code
enum { IMAGE_val_code = 577767 }; // Image validation code
card ncols; // Image width in pixels
card nrows; // Image height in pixels
unsigned long int npixels; // Total no of pixels in the image
char * name; // Image name
card bits_per_pixel; // Image depth
GRAY ** scanrows; // scanrows[i] = &pixels[i,0]
GRAY * pixels; // pixels[i,j] is the
// pixel value at point (i,j)
// Pixels are ordered in rows
void allocate(const card nrows, const card ncols, const card depth);
void _expand(const IMAGE& prototype); // Expand the prototype twice
void _shrink(const IMAGE& prototype); // Shrink the prototype twice
// The following functions create an
// image from a particular file
// format. They call allocate()
// to allocate the image data and
// then read the data. The functions
// are private to an IMAGE(file_name)
// constructor and should be used
// only by it
void read_xwd(EndianIn& file, const bool verbose); // read an X Window dump
void read_pgm(EndianIn& file, const bool verbose);// read a Portable GrayMap
void read_tiff(EndianIn& file, const bool verbose); // read TIFF
public: // Public interface
// Constructors and destructors
// Make a blank image
IMAGE(const card nrows, const card ncols, const card depth);
IMAGE(const IMAGE& image); // Make a new blank image like
// Read an image from the file
// Attempts to detect the file format
IMAGE(const char * file_name,const bool print_header_info = false);
IMAGE(const Rectangle& area); // Make an image from a rect area
// of another image
// Construct an image applying a spec
// operation to the prototype
enum IMAGE_CREATORS_1op { Expand, Shrink };
IMAGE(const IMAGE_CREATORS_1op op, const IMAGE& prototype);
IMAGE(const LazyImage& lazy_constructor);//Make an image using given recipe
~IMAGE(void);
void is_valid(void) const
{ assure(valid_code == IMAGE_val_code,"Invalid image"); }
// Status
card q_nrows() const { return nrows; }
card q_ncols() const { return ncols; }
card q_depth() const { return bits_per_pixel; }
unsigned long int q_npixels() const { return npixels; }
const char * q_name() const { return name; }
void set_name(const char * new_name);
// Individual pixel manipulations
const GRAY operator () (const card row, const card col) const;
GRAY& operator () (const card row, const card col);
const GRAY operator () (const rowcol pos) const;
GRAY& operator () (const rowcol pos);
void sure_within(const rowcol pos) const; // Make sure the pos
// is within the image
// Row Column, Square operations
// Image-scalar operations
// Find out if the predicate
// "(signed)pixel op val" is true for ALL
// pixels of the image?
bool operator == (const int val) const; // ? (signed)pixels == val
bool operator != (const int val) const; // ? (signed)pixels != val
bool operator < (const int val) const; // ? (signed)pixels < val
bool operator <= (const int val) const; // ? (signed)pixels <= val
bool operator > (const int val) const; // ? (signed)pixels > val
bool operator >= (const int val) const; // ? (signed)pixels >= val
// Modify every element of the
// image according to the operation
IMAGE& operator = (const int val); // Assignment to all the pixels
IMAGE& operator -= (const int val); // Diminish the brightness
IMAGE& operator += (const int val); // Increase the brightness
IMAGE& operator *= (const int val);
IMAGE& operator |= (const int val); // OR
IMAGE& operator &= (const int val); // AND
IMAGE& operator ^= (const int val); // XOR
IMAGE& operator <<= (const int val); // Shift all the pixels
IMAGE& operator >>= (const int val); // Shift all the pixels
// Single image operations
IMAGE& clear(void); // Clear the image
IMAGE& invert(void); // Invert the image
IMAGE& abs(void); // pixel = |(signed)pixel|
IMAGE& clip_to_intensity_range(void); // Clip pixel values to
// [0,1<<bits_per_pixel-1]
IMAGE& normalize_for_display(void); // Normalize pixel values to be
// in range 0..1<<bits_per_pixel-1
IMAGE& equalize(const int no_grays); // Perform the histogram equalization
IMAGE& apply(PixelPrimAction& action);// Apply a user-defined action
IMAGE& apply(PixelAction& action); // to each pixel
// Estimate the norm of the (signed) image
double norm_1(void) const; // SUM{ |(signed)pixel[i,j]| }
double norm_2_sqr(void) const; // SUM{ (signed)pixel[i,j]^2 }
int norm_inf(void) const; // MAX{ |(signed)pixel[i,j]| }
// Two images operations
IMAGE& operator = (const IMAGE& source); // Assignment
// Assign another to '*this' resizing
IMAGE& coerce(const IMAGE& another); // '*this' as necessary to fit (by
// not necessarily int factor!)
friend bool operator == (const IMAGE& im1, const IMAGE& im2);
friend void compare(const IMAGE& im1, const IMAGE& im2,
const char * title);
friend inline void are_compatible(const IMAGE& im1, const IMAGE& im2);
// Arithmetics
IMAGE& operator += (const IMAGE& im);
IMAGE& operator -= (const IMAGE& im);
//?? IMAGE& add(IMAGE& target, const int scalar,const IMAGE& source);
// Shift the source to 'pos'
// clip if necessary
// multiply by scalar
// and add
// IMAGE& shift_clip_add(rowcol pos, const int scalar, const IMAGE& source);
// Logic
IMAGE& operator |= (const IMAGE& im);
IMAGE& operator &= (const IMAGE& im);
IMAGE& operator ^= (const IMAGE& im);
// Scalar product
friend double operator * (const IMAGE& im1, const IMAGE& im2);
// Estimate the norm of the difference
// between two (signed) image
// SUM{ |(signed)pixel[i,j]| }
friend double norm_1(const IMAGE& im1, const IMAGE& im2);
// SUM{ (signed)pixel[i,j]^2 }
friend double norm_2_sqr(const IMAGE& im1, const IMAGE& im2);
// MAX{ |(signed)pixel[i,j]| }
friend int norm_inf(const IMAGE& im1, const IMAGE& im2);
// I/O: write, read, display, print info
// Write to a file
// "| command name" is OK as a file
// name
void write(const char * file_name,const char * title = "") const
{ write_xwd(file_name,title); }
// Write an X window dump
void write_xwd(const char * file_name,const char * title = "") const;
// Write a Portable GrayMap
void write_pgm(const char * file_name,const char * title = "") const;
// Write a TIFF file
void write_tiff(const char * file_name,const char * title = "") const;
void display(const char * title) const;
void info(void) const; // Print the info about the image
void print(const char * title) const; // Print the image as a table
// Clip a square area of the image
Rectangle square_of (const card size, const rowcol pos);
// Clip a rectangular area of the image
// specified by the coordinates of
// the upper-left and lower-right corners
Rectangle rectangle (const rowcol uppleft, const rowcol lowright);
};
/*
*------------------------------------------------------------------------
* Position specification and operations on it
*/
class rowcol { // Specifying the row/col position
friend class Extrema;
protected:
short int row_val; // both row, col start from 0
short int col_val;
public:
// Constructors
rowcol(const int row, const int col) : row_val(row), col_val(col) {}
//?? rowcol(void) : row_val(-1), col_val(-1) {}
card row(void) const { return row_val; }
card col(void) const { return col_val; }
bool operator == (const rowcol& pos) const
{ return *((long int *)this) == *((long int *)&pos); }
// { return memcmp(this,&pos,sizeof(rowcol)); }
// Offset the current position
rowcol& operator += (const rowcol& pos)
{ row_val += pos.row_val; col_val += pos.col_val; return *this; }
rowcol& operator -= (const rowcol& pos)
{ row_val -= pos.row_val; col_val -= pos.col_val; return *this; }
friend inline rowcol operator + (const rowcol& pos1, const rowcol& pos2);
friend inline rowcol operator - (const rowcol& pos1, const rowcol& pos2);
// Scale the current position
rowcol& operator *= (const int scalef)
{ row_val *= scalef; col_val *= scalef; return *this; }
friend inline rowcol operator * (const rowcol& pos, const int scalef);
friend inline rowcol operator << (const rowcol& pos, const int shiftf);
friend inline rowcol operator >> (const rowcol& pos, const int shiftf);
};
inline rowcol operator + (const rowcol& pos1, const rowcol& pos2)
{ return rowcol(pos1.row_val+pos2.row_val, pos1.col_val+pos2.col_val);}
inline rowcol operator - (const rowcol& pos1, const rowcol& pos2)
{ return rowcol(pos1.row_val-pos2.row_val, pos1.col_val-pos2.col_val);}
inline rowcol operator * (const rowcol& pos, const int scalef)
{ return rowcol(pos.row_val*scalef, pos.col_val*scalef); }
inline rowcol operator >> (const rowcol& pos, const int shiftf)
{ return rowcol(pos.row_val >> shiftf, pos.col_val >> shiftf); }
inline rowcol operator << (const rowcol& pos, const int shiftf)
{ return rowcol(pos.row_val << shiftf, pos.col_val << shiftf); }
// This is only a specification of a rectangle
// area
class rectarea
{
rowcol upperleft;
rowcol lowright;
public:
// Specify the rect area by the coordinates of
// the upper-left and lower-right corners
rectarea(const rowcol _ul, const rowcol _lr) :
upperleft(_ul), lowright(_lr) {}
};
class Extrema // Find min/max values of the (signed) image
{
GRAY_SIGNED max_value; // Max pixel value in the image
GRAY_SIGNED min_value; // Min pixel value in the image
rowcol max_pixel; // Position of the largest pixel
rowcol min_pixel; // Position of the smallest pixel
public:
Extrema(const IMAGE& image); // Find extreme pixels of the image
GRAY_SIGNED max(void) const { return max_value; }
GRAY_SIGNED min(void) const { return min_value; }
const rowcol& loc_max(void) { return max_pixel; }
const rowcol& loc_min(void) { return min_pixel; }
};
/*
*------------------------------------------------------------------------
* Dealing with the rectangular area of the image
*/
class Rectangle
{
friend class IMAGE;
IMAGE& image; // The image I'm a rectangle of
card nrows; // Dimension of the rectangle
card ncols;
GRAY * ptr; // Pointer to the upper left corner of
// the rectangle
int inc_to_nextrow; // inc_to_nextrow = p' - p =
// = image.ncols - ncols
// where
// p = ptr + ncols points to the pixel
// just beyond the current scanline of
// the Rectangle
// p' = (ptr - col) + image.ncols + col
// points to the first pixel of the next
// scanline of the rectangtle
// col tells the column of the upper left
// corner of the rectangle within the image
// Private constructor
// Note these are private constructors to
// be used with IMAGE::square_of/rectangle
// functions
Rectangle
(IMAGE& im, const card size, const rowcol pos);
Rectangle
(IMAGE& im, const rowcol uppleft, const rowcol lowright);
public:
// Note how to construct square area of
// the image 'im':
// im.square_of(10,rowcol(1,5))
// and rectangular area
// im.rectangle(rowcol(0,1),rowcol(4,5))
Rectangle (IMAGE& im); // Treat entire image as a rectangle
// Assign a value to all the pixels
// in the rectangle area
Rectangle& operator = (const int val);
// Modify the pixels in the rectangle
// area
Rectangle& operator += (const int val);
Rectangle& operator -= (const int val);
Rectangle& operator *= (const int val);
Rectangle& operator |= (const int val);
Rectangle& operator &= (const int val);
Rectangle& operator ^= (const int val);
Rectangle& operator <<= (const int val);
Rectangle& operator >>= (const int val);
// Copy a rectangle a_rect into the
// rectangular area of the image
Rectangle& operator = (const Rectangle& a_rect);
// Get a total sum of all the pixels
friend double sum_over(const Rectangle& sa);
};
/*
*------------------------------------------------------------------------
* Some service procedures
*/
inline
int log2( // Find a binary logarithm of n
const int n ) // and check that n is a power of two
{
register int i,k;
assure(n > 0, "log2: the argument's got to be positive!");
for(n & 0xff ? k=0,i=1 : k=8,i=256; i < n; k++, i*=2)
;
if( i != n )
_error("log2: the argument %d has got to be an exact power of two",n);
return k;
}
inline
int exp2(const int k) // Compute 2^k, k>=0
{
assure(k >= 0, "exp2: the argument may not be negative!");
if( k > (signed)sizeof(int)*8-1 )
_error("exp2: the exponent %d is too big",k);
return 1<<k;
}
inline // return b * round( a/b )
int round_to_even_multiple(const int a, const int b)
{
assert( b > 0 );
register int rem = a % b;
if( 2*rem < b )
return a - rem;
else
return a + b - rem;
}
// Service functions (useful in the
// verification code). They print some detail
// info if the validation condition fails
void verify_pixel_value(const IMAGE& im, const GRAY val);
/*
*------------------------------------------------------------------------
* Inline Image Procedures
*/
inline IMAGE::IMAGE(const card no_rows, const card no_cols, const card depth)
{
allocate(no_rows,no_cols,depth);
}
// Make a new image like the
inline IMAGE::IMAGE(const IMAGE& old) // old one
{
allocate(old.nrows,old.ncols,old.bits_per_pixel);
}
inline IMAGE::IMAGE(const LazyImage& lazy_constructor)
{
allocate(lazy_constructor.nrows,lazy_constructor.ncols,
lazy_constructor.depth);
lazy_constructor.fill_in(*this);
}
inline GRAY& IMAGE::operator () (const card row, const card col)
{
is_valid();
if( row >= nrows )
_error("Row index %d is out of image boundaries [0,%d]",row,nrows-1);
if( col >= ncols )
_error("Col index %d is out of image boundaries [0,%d]",col,ncols-1);
return (scanrows[row])[col];
}
inline const GRAY IMAGE::operator () (const card row, const card col) const
{ return (*(IMAGE *)this).operator()(row,col); }
inline const GRAY IMAGE::operator () (const rowcol pos) const
{
return operator()(pos.row(),pos.col());
}
inline GRAY& IMAGE::operator () (const rowcol pos)
{
return operator()(pos.row(),pos.col());
}
// Make sure the pos is within the
// image
inline void IMAGE::sure_within(const rowcol pos) const
{
is_valid();
if( pos.row() >= nrows )
_error("Row index %d is out of image boundaries [0,%d]",pos.row(),nrows-1);
if( pos.col() >= ncols )
_error("Col index %d is out of image boundaries [0,%d]",pos.col(),ncols-1);
}
inline IMAGE& IMAGE::clear(void) // Clean the image
{
is_valid();
memset(pixels,0,npixels*sizeof(GRAY));
return *this;
}
inline IMAGE& IMAGE::invert(void) // Invert the image
{
is_valid();
return (*this ^= ((1<<bits_per_pixel)-1));
}
inline void are_compatible(const IMAGE& im1, const IMAGE& im2)
{
im1.is_valid();
im2.is_valid();
if( im1.ncols != im2.ncols || im1.nrows != im2.nrows )
_error("The image %dx%d and the image %dx%d have different sizes",
im1.nrows,im1.ncols,im2.nrows,im2.ncols);
}
// Apply a user-defined action to each pixel
inline IMAGE& IMAGE::apply(PixelPrimAction& action)
{
is_valid();
for(register GRAY *pp=pixels; pp < pixels+npixels; pp++)
action.operation(*pp);
return *this;
}
// Construct a square rectangle
inline Rectangle::Rectangle
(IMAGE& im, const card sq_size, const rowcol pos)
: image(im), nrows(sq_size), ncols(sq_size)
{
image.is_valid();
if( pos.row() >= image.nrows ||
pos.row() + nrows > image.nrows ||
pos.col() >= image.ncols ||
pos.col() + ncols > image.ncols )
_error("Square area (left upper point [%d,%d], size %d)\n"
"is not within the image %dx%d",
pos.row(),pos.col(),sq_size,image.nrows,image.ncols);
ptr = &(image.scanrows[pos.row()][pos.col()]);
inc_to_nextrow = image.ncols - ncols;
}
// Clip a square area from the image
inline Rectangle IMAGE::square_of(const card size, const rowcol pos)
{ return Rectangle(*this, size, pos); }
// Make a rectangle with upper left corner
// at uppleft position and lower right corner
// at lowright position
inline Rectangle::Rectangle
(IMAGE& im, const rowcol uppleft, const rowcol lowright)
: image(im), nrows(lowright.row()-uppleft.row()+1),
ncols(lowright.col()-uppleft.col()+1)
{
image.is_valid();
image.sure_within(uppleft);
image.sure_within(lowright);
ptr = &(image.scanrows[uppleft.row()][uppleft.col()]);
inc_to_nextrow = image.ncols - ncols;
}
// Clip a rectangular area from the image
inline Rectangle IMAGE::rectangle(const rowcol uppleft,const rowcol lowright)
{ return Rectangle(*this, uppleft, lowright); }
// Treat the entire image as a rectangular area
inline Rectangle::Rectangle (IMAGE& im)
: image(im), nrows(im.nrows), ncols(im.ncols)
{
image.is_valid();
ptr = image.pixels;
inc_to_nextrow = 0;
}
#endif